import { IVec2 } from "../Interface/State";
import { circle } from "../QuadTree";
import { RVOMath, Vector2 } from "../RVO/Common";

export const strencode = (str: string) => {
    let byteArray: number[] = [];
    for (let i = 0; i < str.length; i++) {
        let charCode = str.charCodeAt(i);
        if (charCode <= 0x7f) {
            byteArray.push(charCode);
        } else if (charCode <= 0x7ff) {
            byteArray.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f));
        } else if (charCode <= 0xffff) {
            byteArray.push(0xe0 | (charCode >> 12), 0x80 | ((charCode & 0xfc0) >> 6), 0x80 | (charCode & 0x3f));
        } else {
            byteArray.push(
                0xf0 | (charCode >> 18),
                0x80 | ((charCode & 0x3f000) >> 12),
                0x80 | ((charCode & 0xfc0) >> 6),
                0x80 | (charCode & 0x3f)
            );
        }
    }
    return new Uint8Array(byteArray);
};

export const strdecode = (bytes: Uint8Array): string => {
    let array: number[] = [];
    let offset = 0;
    let charCode = 0;
    let end = bytes.length;
    while (offset < end) {
        if (bytes[offset] < 128) {
            charCode = bytes[offset];
            offset += 1;
        } else if (bytes[offset] < 224) {
            charCode = ((bytes[offset] & 0x3f) << 6) + (bytes[offset + 1] & 0x3f);
            offset += 2;
        } else if (bytes[offset] < 240) {
            charCode = ((bytes[offset] & 0x0f) << 12) + ((bytes[offset + 1] & 0x3f) << 6) + (bytes[offset + 2] & 0x3f);
            offset += 3;
        } else {
            charCode =
                ((bytes[offset] & 0x07) << 18) +
                ((bytes[offset + 1] & 0x3f) << 12) +
                ((bytes[offset + 1] & 0x3f) << 6) +
                (bytes[offset + 2] & 0x3f);
            offset += 4;
        }
        array.push(charCode);
    }
    return String.fromCharCode.apply(null, array);
};

/** 深拷贝 */
export const deepClone = (obj: any) => {
    if (obj === null || typeof obj !== 'object') {
        return obj
    }
    const res = Array.isArray(obj) ? [] : {} as any
    for (const key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            res[key] = deepClone(obj[key]) as any
        }
    }
    return res
}

/** 角度转弧度 */
export const angelToRadian = (angle: number): number => {
    return toFixed(toFixed(angle / 180) * Math.PI)
}

/** 弧度转角度 */
export const radianToAngel = (radian: number): number => {
    return toFixed(toFixed(radian * 180) / Math.PI)
}

/** 向量转角度 */
export const vecToAngel = (x: number, y: number) => {
    // 使用Math.atan2()函数将向量转换为弧度值
    let radian = toFixed(Math.atan2(y, x));
    // 将弧度值转换为角度值
    return radianToAngel(radian);
}

/**
 * 截取数字小数点
 * @param num 
 * @param digit 保留的小数位，默认4
 * @returns 
 */
export const toFixed = (num: number, digit = 4): number => {
    return Math.round(num * 10 ** digit) / 10 ** digit
}

/** 通过种子获取随机数 */
export const randomBySeed = (seed: number) => {
    return (seed * 9301 + 49297) % 233280
}

/** 两点距离的平方 */
export const distanceSqu = (p1: Vector2 | IVec2, p2: Vector2 | IVec2) => {
    const x = p1.x - p2.x;
    const y = p1.y - p2.y;
    return toFixed(x * x) + toFixed(y * y);
}

/** 两点间距离 */
export const distance = (p1: Vector2 | IVec2, p2: Vector2 | IVec2) => {
    return toFixed(Math.sqrt(distanceSqu(p1, p2)));
}

/** v 按照 o 为圆心 旋转 a 弧度 */
export const rotate = (v: Vector2, o: Vector2, a: number) => {
    const x = v.x - o.x;
    const y = v.y - o.y;

    const cos = toFixed(Math.cos(a));
    const sin = toFixed(Math.sin(a));
    const rx = toFixed(x * cos) - toFixed(y * sin);
    const ry = toFixed(x * sin) + toFixed(y * cos);

    return new Vector2(toFixed(rx + o.x), toFixed(ry + o.y));
}

/**
 *  获取 p1 比 p2 距离p3远
 *  true:p1更远
 */
export const get3PointDisFar = (p1: Vector2, p2: Vector2, p3: Vector2) => {
    const v1 = new Vector2(p1.x, p1.y)
    const v2 = new Vector2(p2.x, p2.y)
    const v3 = new Vector2(p3.x, p3.y)
    return v3.clone().minus(v1).lengthSqr() > v3.clone().minus(v2).lengthSqr()
}

/**
 * 获得圆心到点的向量，与外圆相交的点
 * @param p1 点
 * @param p2 圆心
 * @param radius 圆半径
 * @returns Vector2
 */
export const getPointInCircleByPoint = (p1: Vector2 | IVec2, p2: circle): Vector2 => {
    const { p: { x, y }, r } = p2

    const point = new Vector2(p1.x, p1.y)
    const center = new Vector2(x, y)

    let v2 = RVOMath.normalize(point.minus(center))

    return v2.scale(r).plus(center)
}

/** 用于将坐标 a 和 b 绕中点 c 进行旋转 */
export const rotateAroundCenter = (a: Vector2, b: Vector2, angleDegrees: number, dis: number = 300): [Vector2, Vector2] => {
    // 计算中点 c
    const c = {
        x: (a.x + b.x) / 2,
        y: (a.y + b.y) / 2
    };

    // 将坐标 a 和 b 按照中点 c 旋转 angleDegrees 度
    const angleRadians = toFixed(angleDegrees * (Math.PI / 180))
    const cosTheta = toFixed(Math.cos(angleRadians));
    const sinTheta = toFixed(Math.sin(angleRadians));

    const translatedAx = a.x - c.x;
    const translatedAy = a.y - c.y;
    const rotatedAx = toFixed(translatedAx * cosTheta) - toFixed(translatedAy * sinTheta);
    const rotatedAy = toFixed(translatedAx * sinTheta) + toFixed(translatedAy * cosTheta);
    const newAx = rotatedAx + c.x;
    const newAy = rotatedAy + c.y;

    const translatedBx = b.x - c.x;
    const translatedBy = b.y - c.y;
    const rotatedBx = toFixed(translatedBx * cosTheta) - toFixed(translatedBy * sinTheta);
    const rotatedBy = toFixed(translatedBx * sinTheta) + toFixed(translatedBy * cosTheta);
    const newBx = rotatedBx + c.x;
    const newBy = rotatedBy + c.y;

    const currentDistance = distance(new Vector2(c.x, c.y), new Vector2(newAx, newAy));
    const ratio = toFixed(dis / currentDistance);

    const movedA = {
        x: c.x + toFixed((newAx - c.x) * ratio),
        y: c.y + toFixed((newAy - c.y) * ratio),
    };

    const movedB = {
        x: c.x + toFixed((newBx - c.x) * ratio),
        y: c.y + toFixed((newBy - c.y) * ratio),
    };

    return [new Vector2(movedA.x, movedA.y), new Vector2(movedB.x, movedB.y)];
}

/** 获取点到点方向 */
export const getDirByPos = (x1: number, y1: number, x2: number, y2: number) => {
    const p1 = new Vector2(x1, y1)
    const p2 = new Vector2(x2, y2)
    const p3 = RVOMath.normalize(p1.minus(p2))
    return p3
}